<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        li {
            list-style: none;
        }

        .tab {
            width: 500px;
            height: 300px;
            border: 1px solid #ccc;
            margin: 50px auto;
        }

        .tab>* {
            padding: 10px;
        }

        .tab>.tabNav {
            display: flex;
            justify-content: space-between;
            align-items: center;
            border-bottom: 1px solid #ccc;
        }

        .tab>.tabNav>.add {
            font-size: 20px;
            cursor: pointer;
        }

        .tab>.tabNav>ul {
            display: flex;
            align-items: center;
            height: 40px;
        }

        .tab>.tabNav>ul>li {
            position: relative;
            margin-right: 10px;
            padding: 10px;
            height: 100%;
        }

        .tab>.tabNav>ul>li>.close {
            position: absolute;
            top: -5px;
            right: -5px;
            cursor: pointer;
        }

        .tab>.tabNav>ul>li.active {
            border-bottom: 3px solid pink;
        }

        .tab>.tabContent>div {
            display: none;
        }

        .tab>.tabContent>div.current {
            display: block;
        }
    </style>

    <script>
        document.addEventListener('DOMContentLoaded', function () {
            class Tab {
                constructor(id) {
                    this.tab = document.querySelector(id);
                    this.tabNav = this.tab.querySelector('.tabNav');
                    this.tabContent = this.tab.querySelector('.tabContent');
                    this.init();
                    this.dispatch();
                }

                // tab索引
                init() {
                    let elem = this.tabNav.querySelector('ul');
                    [...elem.children].forEach((el, i) => el.dataset.index = i);
                    [...this.tabContent.children].forEach((el, i) => el.dataset.index = i);
                    if (![...elem.children].some(el => el.className == 'active')) {
                        elem.children[elem.children.length - 1] && (elem.children[elem.children.length - 1].className = 'active');
                        this.tabContent.children[this.tabContent.children.length - 1] && (this.tabContent.children[this.tabContent.children.length - 1].className = 'current');
                    }
                }

                // 事件派发
                dispatch() {
                    // 事件委托
                    this.tab.addEventListener('click', e => e.target.className && ['toggle', 'add', 'close'].includes(e.target.className) && this[e.target.className](e.target));
                    // 双击修改
                    this.tab.addEventListener('dblclick', e => (e.target.className == 'toggle' || e.target.nodeName == 'DIV') && this.update(e.target));
                }

                // 切换tab
                toggle(target) {
                    // 排它
                    [...target.parentNode.parentNode.children].forEach((el, i) => el.className = '');
                    target.parentNode.className = 'active';
                    // 激活索引
                    let index = [...target.parentNode.parentNode.children].findIndex(el => el.className == 'active');
                    this.tabContent.querySelector('.current').className = '';
                    this.tabContent.children[index].className = 'current';
                }

                // 添加tab
                add(target) {
                    let elem = this.tabNav.querySelector('ul');
                    let index = elem.children[elem.children.length - 1] ? elem.children[elem.children.length - 1].dataset.index : 0;
                    let n = elem.children.length;
                    [...elem.children].forEach(el => el.className = '');
                    elem.insertAdjacentHTML('beforeend', `<li class="active" data-index="${index}"><span class="toggle">测试${n + 1}</span><span class="close">&times;</span></li>`);
                    [...this.tabContent.children].forEach(el => el.className = '');
                    this.tabContent.insertAdjacentHTML('beforeend', `<div class="current" data-index="${index}">测试${n + 1}</div>`);
                }

                // 关闭tab
                close(target) {
                    let elem = this.tabNav.querySelector('ul');
                    let index = target.parentNode.dataset.index;
                    [...elem.children].find(el => el.dataset.index == index).remove();
                    [...this.tabContent.children].find(el => el.dataset.index == index).remove();
                    // 重拍索引
                    this.init();
                }

                // 修改tab
                update(target) {
                    // 双击禁止选定文字
                    window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
                    let str = target.textContent;
                    target.innerHTML = `<input type="text" value="${str}">`;
                    let input = target.querySelector('input[type="text"]');
                    input.onblur = () => input.parentNode.innerHTML = input.value;
                    input.onkeyup = (e) => e.keyCode == 13 && input.blur();
                }
            }
            // 实例化
            tab = new Tab('#tab');
        });
    </script>
</head>

<body>
    <div class="tab" id="tab">
        <div class="tabNav">
            <ul>
                <li class="active"><span class="toggle">测试1</span><span class="close">&times;</span></li>
                <li><span class="toggle">测试2</span><span class="close">&times;</span></li>
                <li><span class="toggle">测试3</span><span class="close">&times;</span></li>
            </ul>
            <p class="add">+</p>
        </div>
        <div class="tabContent">
            <div class="current">测试1</div>
            <div>测试2</div>
            <div>测试3</div>
        </div>
    </div>
</body>

</html>